﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Algorithm;
using NetTopologySuite.Geometries;

namespace NetTopologySuite.Precision
{
	public class SimpleMinimumClearance
	{
		public static double GetDistance(IGeometry g)
		{
			return new SimpleMinimumClearance(g).GetDistance();
		}

		public static IGeometry GetLine(IGeometry g)
		{
			return new SimpleMinimumClearance(g).GetLine();
		}

		public SimpleMinimumClearance(IGeometry geom)
		{
			this._inputGeom = geom;
		}

		public double GetDistance()
		{
			this.Compute();
			return this._minClearance;
		}

		public ILineString GetLine()
		{
			this.Compute();
			return this._inputGeom.Factory.CreateLineString(this._minClearancePts);
		}

		private void Compute()
		{
			if (this._minClearancePts != null)
			{
				return;
			}
			this._minClearancePts = new Coordinate[2];
			this._minClearance = double.MaxValue;
			this._inputGeom.Apply(new SimpleMinimumClearance.VertexCoordinateFilter(this, this._inputGeom));
		}

		private void UpdateClearance(double candidateValue, Coordinate p0, Coordinate p1)
		{
			if (candidateValue < this._minClearance)
			{
				this._minClearance = candidateValue;
				this._minClearancePts[0] = new Coordinate(p0);
				this._minClearancePts[1] = new Coordinate(p1);
			}
		}

		private void UpdateClearance(double candidateValue, Coordinate p, Coordinate seg0, Coordinate seg1)
		{
			if (candidateValue < this._minClearance)
			{
				this._minClearance = candidateValue;
				this._minClearancePts[0] = new Coordinate(p);
				LineSegment lineSegment = new LineSegment(seg0, seg1);
				this._minClearancePts[1] = new Coordinate(lineSegment.ClosestPoint(p));
			}
		}

		private readonly IGeometry _inputGeom;

		private double _minClearance;

		private Coordinate[] _minClearancePts;

		private class VertexCoordinateFilter : ICoordinateFilter
		{
			public VertexCoordinateFilter(SimpleMinimumClearance smc, IGeometry inputGeometry)
			{
				this._smc = smc;
				this._inputGeometry = inputGeometry;
			}

			public void Filter(Coordinate coord)
			{
				this._inputGeometry.Apply(new SimpleMinimumClearance.ComputeMCCoordinateSequenceFilter(this._smc, coord));
			}

			private readonly SimpleMinimumClearance _smc;

			private readonly IGeometry _inputGeometry;
		}

		private class ComputeMCCoordinateSequenceFilter : ICoordinateSequenceFilter
		{
			public ComputeMCCoordinateSequenceFilter(SimpleMinimumClearance smc, Coordinate queryPt)
			{
				this._smc = smc;
				this._queryPt = queryPt;
			}

			public void Filter(ICoordinateSequence seq, int i)
			{
				this.CheckVertexDistance(seq.GetCoordinate(i));
				if (i > 0)
				{
					this.CheckSegmentDistance(seq.GetCoordinate(i - 1), seq.GetCoordinate(i));
				}
			}

			private void CheckVertexDistance(Coordinate vertex)
			{
				double num = vertex.Distance(this._queryPt);
				if (num > 0.0)
				{
					this._smc.UpdateClearance(num, this._queryPt, vertex);
				}
			}

			private void CheckSegmentDistance(Coordinate seg0, Coordinate seg1)
			{
				if (this._queryPt.Equals2D(seg0) || this._queryPt.Equals2D(seg1))
				{
					return;
				}
				double num = CGAlgorithms.DistancePointLine(this._queryPt, seg1, seg0);
				if (num > 0.0)
				{
					this._smc.UpdateClearance(num, this._queryPt, seg1, seg0);
				}
			}

			public bool Done
			{
				get
				{
					return false;
				}
			}

			public bool GeometryChanged
			{
				get
				{
					return false;
				}
			}

			private readonly SimpleMinimumClearance _smc;

			private readonly Coordinate _queryPt;
		}
	}
}
